#include <g-bios.h>
#include <core/cmd.h>
#include <flash/flash.h>
#include <flash/part.h>
#include <core/getopt.h>
#include <flash/part.h>


static void FlashDumpUsage(void)
{
	printf("Usage: flashdump [options <value>]\n"
		   "\noptions:\n"
	    	   "  -b   \t\tset flash block number\n"
           	   "  -p   \t\tset flash page number\n"
           	   "  -a   \t\tset flash address\n" 
       	   );
}


static int main(int argc, char *argv[])
{
	int    ch, ret;
	BYTE  *p, *pbBufAddr, bFlag;
	ULONG  ulStart, ulSize;
	struct Flash *pFlash;
	struct Partition *pCurPart;
	char *optarg;


	// fixme: pFlash location
	pCurPart = GuPartOpenCur();
	BUG_IF(NULL == pCurPart);

	pFlash = pCurPart->pHost;
	BUG_IF(NULL == pFlash);

	ulSize  = pFlash->nWriteSize + pFlash->nOobSize;
	ulStart = GuPartGetBase(pCurPart);

	GuPartClose(pCurPart);


	bFlag = 0;
	while ((ch = getopt(argc, argv, "b:p:a:h", &optarg)) != -1)
	{
		switch (ch)
		{
		case 'b':
			if (bFlag || GuStrToVal(optarg, &ulStart) < 0)
			{
				printf("Invalid argument: \"%s\"\n", optarg);

				FlashDumpUsage();

				return -EINVAL;
			}

			ulStart = ulStart * pFlash->nBlockSize;

			if (ulStart >= pFlash->nChipSize)
			{
				printf("Block number 0x%08x overflow!\n", ulStart);
				return -EINVAL;
			}

			bFlag = 1;

			break;

		case 'a':
			if (bFlag || GuStrToVal(optarg, &ulStart) < 0)
			{
				printf("Invalid argument: \"%s\"\n", optarg);

				FlashDumpUsage();

				return -EINVAL;
			}

			if (ulStart >= pFlash->nChipSize)
			{
				printf("Address 0x%08x overflow!\n", ulStart);
				return -EINVAL;
			}

			bFlag = 1;

			break;

		case 'p':
			if (bFlag || GuStrToVal(optarg, &ulStart) < 0)
			{
				printf("Invalid argument: \"%s\"\n", optarg);
				FlashDumpUsage();

				return -EINVAL;
			}

			ulStart = ulStart * pFlash->nPageSize;

			if (ulStart >= pFlash->nChipSize)
			{
				printf("Page number 0x%08x overflow!\n", ulStart);
				return -EINVAL;
			}

			bFlag = 1;

			break;

		case 'h':
			FlashDumpUsage();
			return 0;

		default:
			FlashDumpUsage();
			return -EINVAL;
		}
	}


	pbBufAddr = (BYTE *)malloc(ulSize);
	if (NULL == pbBufAddr)
	{
		return -ENOMEM;
	}

	ret = GuFlashReadRaw(pFlash, pbBufAddr, (int *)&ulStart, ulSize);
	if (ret < 0)
	{
		printf("%s(): line %d execute GuFlashReadRaw() error!\n"
			"error = %d\n", __FUNCTION__, __LINE__, ret);

		free(pbBufAddr);

		return ret;
	}

	DPRINT("Flash 0x%08x ==> RAM 0x%08x, Expected length 0x%08x, Real length 0x%08x\n\n",
		   ulStart, pbBufAddr, ulSize, ret);

	p = pbBufAddr;

	while (ulSize > 0)
	{
		int i;

		printf("Page @ 0x%08x:\n", ulStart);

		i = pFlash->nWriteSize >> 4; 

		while (i--) 
		{
			printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x"
				"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);

			p += 16;
		}

		printf("OOB @ 0x%08x:\n", ulStart);

		i = pFlash->nOobSize >> 4;

		while (i--) 
		{
			printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x"
				"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);

			p += 16;
		}

		ulStart += pFlash->nWriteSize + pFlash->nOobSize;
		ulSize  -= pFlash->nWriteSize + pFlash->nOobSize;

		puts("\n");
	}

	free(pbBufAddr);

	return 0;
}


INSTALL_APPLICATION(flashdump, main, "flash dump");

